#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>

#include <glib.h>
#include <glib/gprintf.h>
 

static int pfds[2];

typedef struct _MySource MySource;
struct _MySource {
    GSource source;
    time_t ready_time;
};
 
static gboolean my_source_prepare(GSource *source, gint *timeout) {
    struct timespec tp;
    MySource *my_src = (MySource *) source;

    g_print("calling %s\n", __func__);

    clock_gettime(CLOCK_MONOTONIC, &tp);
    if(my_src->ready_time == 0) 
        my_src->ready_time = tp.tv_sec+3;
    *timeout = 1000;

    return FALSE;
}
 
static gboolean my_source_check(GSource *source) {
    struct timespec tp;
    MySource *my_src = (MySource *) source;

    g_print("calling %s\n", __func__);

    clock_gettime(CLOCK_MONOTONIC, &tp);

    if(my_src->ready_time <= tp.tv_sec ) {
        my_src->ready_time = 0;
        return TRUE;
    }else {
        return FALSE;
    }
}
 
static gboolean my_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    MySource *mysource = (MySource *)source;

    g_print("calling %s\n", __func__);
 
    if(callback) callback(user_data);
 
    return G_SOURCE_CONTINUE;
}
 

//idle
static gboolean idle_callback(gpointer data) {
    g_print("calling %s\n", __func__);
    return G_SOURCE_REMOVE;
}

//Timeout callback
static gboolean timeout_callback(gpointer data) {
    static guint i = 0;
    int fd = 0;
    char buf[64];
    int len = 0;
    g_print("calling %s\n", __func__);
    i += 2;
    if(pfds[1]) {
        len = sprintf(buf, "%d", i);
        //fd = open("/tmp/tpipe", O_WRONLY);
        write(pfds[1], buf, len);
    }
    if(i%4) {
        int src_id = g_idle_add(idle_callback, NULL);    
        GSource *idle_src = 
            g_main_context_find_source_by_id(
                g_main_context_default(),
                src_id);
        g_source_set_priority(idle_src, G_PRIORITY_HIGH_IDLE);
    }
 
    return G_SOURCE_CONTINUE;
}

//unix fd source
typedef struct {
    GSource _source;
    GIOCondition pollevents;
    gpointer tag;
    int fd;
}UnixFdSource;

//prepare
static gboolean unix_fd_source_prepare(GSource *source, gint *timeout) {
    g_print("calling %s\n", __func__);
    *timeout = -1;
    return FALSE;
}

//check
static gboolean unix_fd_source_check(GSource *source) {
    UnixFdSource *src = (UnixFdSource *)source;
    g_print("calling %s\n", __func__);
    GIOCondition revents = g_source_query_unix_fd(source, src->tag);
    return revents & (G_IO_IN|G_IO_ERR);
    
}

//dispatch
static gboolean unix_fd_source_dispatch(GSource *source, GSourceFunc callback, gpointer usr_data) {
    UnixFdSource *src = (UnixFdSource *)source; 

    g_print("calling %s\n", __func__);
    
    GIOCondition revents = g_source_query_unix_fd(source, src->tag);
    if(revents & G_IO_ERR) 
        return  G_SOURCE_REMOVE;
    

    if(callback) 
        return callback(usr_data);
    
 
    return G_SOURCE_CONTINUE;
}

//callback
static gboolean  unix_fd_source_callback(gpointer data) {
    static const int maxbuf = 128;
    g_print("calling %s\n", __func__);
    gchar buf[maxbuf+1];
    buf[0] = '\0';

    int n;
    while((n=read(((UnixFdSource*)data)->fd, buf, maxbuf)) > 0) {
        buf[n] = '\0';
        g_print("%s", buf); 
    }
    g_print("\n"); 
    if(buf[0]) {
        return G_SOURCE_CONTINUE;
    } else {
        return G_SOURCE_REMOVE;
    }
}


//finalize
void unix_fd_source_finalize (GSource    *source) {
    UnixFdSource *src = (UnixFdSource *)source; 
    g_print("calling %s\n", __func__);
    if(src->fd != -1) {
        close(src->fd);
        src->fd = -1;
    }
}



int main(int argc, char *argv[])
{
    GMainContext *manual_context = g_main_context_new();
#ifdef GLOBAL_DEFAULT_CONTEXT
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainLoop *loop2 = g_main_loop_new(NULL, TRUE);
    g_print("default loop:%p, loop2:%p\n", loop, loop2);
    g_main_loop_unref(loop2);
#else
    GMainLoop *loop = g_main_loop_new(manual_context, TRUE);
#endif 
    GMainContext *loop_context = g_main_loop_get_context(loop);
    GMainContext *main_default_context = g_main_context_default();
    GMainContext *main_thread_default_context = g_main_context_get_thread_default ();

    g_print("manual_context:%p, loop_context:%p, main_default_context:%p, "
            "main_thread_default_context:%p\n", 
            manual_context, loop_context, main_default_context, main_thread_default_context);

    GSourceFuncs source_funcs = {my_source_prepare, my_source_check, my_source_dispatch, NULL};
    GSource *source = g_source_new(&source_funcs, sizeof(MySource));
    ((MySource *)source)->ready_time = 0;
    g_source_attach(source, loop_context);
    g_source_unref(source);
 
    
    g_timeout_add(2000, timeout_callback, loop);    
    
    GSourceFuncs unix_fd_source_funcs = {
        unix_fd_source_prepare,
        unix_fd_source_check,
        unix_fd_source_dispatch,
        unix_fd_source_finalize
    };

    
    GSource *src2 = g_source_new(&unix_fd_source_funcs, sizeof(UnixFdSource));
    UnixFdSource *unix_fd_src = (UnixFdSource *) src2;
    
    if(pipe(pfds)) 
        g_source_unref(src2); 
    else {
        fcntl(pfds[0], F_SETFL,  fcntl(pfds[0], F_GETFL)|O_NONBLOCK);
        unix_fd_src->fd = pfds[0];
        unix_fd_src->pollevents = G_IO_IN|G_IO_ERR;
        unix_fd_src->tag = g_source_add_unix_fd(src2, unix_fd_src->fd, unix_fd_src->pollevents);
        g_source_set_callback(src2, unix_fd_source_callback, src2, NULL);
        //g_source_set_priority(src2, G_PRIORITY_DEFAULT_IDLE);
        g_source_attach(src2, loop_context);
    }
    
    g_main_loop_run(loop);
 
    g_main_loop_unref(loop);
    g_main_context_unref(manual_context);
 
    return 0;
}
